from feature_data.const_data import *
from feature_data.XmlData2DictData import Xml2Dict
from feature_data.Dict2Coauthor_Graphfeature import Feature_coauthor_extration
from nd_service.first_stage_graph import Graph_coauthor_stage
from nd_utils.vec_similarity import set_jaccrd


# https://blog.csdn.net/xiaqian0917/article/details/53445071

# macro micro https://blog.csdn.net/Leoch007/article/details/80684464
class Metric_macro_micro():
    def __init__(self, xml2dict):
        self.xml2dict = xml2dict

        self.publication_dict = self.xml2dict.publication_dict
        self.paperid_idx_dict = self.xml2dict.paperid_idx_dict
        self.idx_paperid_dict = self.xml2dict.idx_paperid_dict

        self.labelset_list = self._get_labelset_list()
        self.paper_label_dict = self._get_paper_label_dict()
        # true
        self.true_label_dict = self._get_label_inverse_table()

        self.cluster_list = self._get_cluster_list()
        self.clusterlen_sort_label_list = self._get_clusterlen_sort_label_list()
        # predict
        self.predict_label_dict = self._get_predict_label_dict()

    """
        idx : label
    """

    def _get_paper_label_dict(self):
        paper_label_dict = {}
        for paperid, publication_attr_dict in self.publication_dict.items():
            paper_label_dict[self.paperid_idx_dict[paperid]] = publication_attr_dict['label']
        return paper_label_dict

    """
        all label set
    """

    def _get_labelset_list(self):
        label_set = set()
        for publication_attr_dict in self.publication_dict.values():
            label_set.add(publication_attr_dict['label'])
        return list(label_set)

    def _get_label_inverse_table(self):
        label_idx_dict = {}
        for paper_idx, paper_label in self.paper_label_dict.items():
            if paper_label not in label_idx_dict:
                label_idx_dict[paper_label] = [paper_idx]
            else:
                label_idx_dict[paper_label].append(paper_idx)
        return label_idx_dict

    def _get_cluster_list(self):
        feature_coauthor_extration = Feature_coauthor_extration(self.xml2dict)
        second_stage_clust = Graph_coauthor_stage(feature_coauthor_extration)
        return second_stage_clust.subgraph_list

    # dict sort by values
    # https://www.cnblogs.com/timtike/p/6562402.html
    def _get_clusterlen_sort_label_list(self):

        # true_label_dict = dict(self.true_label_dict)

        label_length_dict = {}
        for label, cluster in self.true_label_dict.items():
            label_length_dict[label] = len(cluster)
        clusterlen_sort_label_list = []
        for label, length in sorted(label_length_dict.items(), key=lambda x: x[1], reverse=True):
            clusterlen_sort_label_list.append(label)
        return clusterlen_sort_label_list

    def get_max_jaccard_set(self, true_idx_set, cluster_set_list):
        max_jaccard_similarity = 0
        temp_cluster_set = None
        for cluster_set in cluster_set_list:
            if set_jaccrd(true_idx_set, cluster_set) > max_jaccard_similarity:
                max_jaccard_similarity = set_jaccrd(true_idx_set, cluster_set)
                temp_cluster_set = cluster_set
        if temp_cluster_set != None:
            return temp_cluster_set
        else:
            return set([])

    def get_cluster_set_list(self):
        cluster_list = list(self.cluster_list)
        cluster_set_list = []
        for cluster in cluster_list:
            cluster_set_list.append(set(cluster))
        return cluster_set_list

    def _get_predict_label_dict(self):
        true_label_dict = self.true_label_dict
        predict_label_dict = {}

        cluster_set_list = self.get_cluster_set_list()
        for label in self.clusterlen_sort_label_list:
            cluster_set = self.get_max_jaccard_set(set(true_label_dict[label]), cluster_set_list)
            predict_label_dict[label] = list(cluster_set)
            if len(cluster_set) != 0:
                cluster_set_list.remove(cluster_set)
        return predict_label_dict

    def print_true_predict_lable_dict(self):
        label_list = self.true_label_dict.keys()
        sum_f1 = 0
        for label in label_list:
            predict_list = self.predict_label_dict[label]
            true_list = self.true_label_dict[label]
            print label, true_list, predict_list

    # p : 3p
    def get_precision(self, true_list, predict_list):
        tp = len(set(predict_list).intersection(set(true_list)))
        tp_plus_fp = len(predict_list)
        return float(tp) / tp_plus_fp

    # p : 2p1n
    def get_recall(self, true_list, predict_list):
        tp = len(set(predict_list).intersection(set(true_list)))
        tp_plust_fn = len(true_list)
        return float(tp) / tp_plust_fn

    def get_f1(self, true_list, predict_list):
        precision = self.get_precision(true_list, predict_list)
        recall = self.get_recall(true_list, predict_list)
        return float(2 * precision * recall) / (precision + recall)

    # cluster_list must be enough

    def get_macro_f1(self):
        label_list = self.true_label_dict.keys()
        sum_f1 = 0
        f1_num = 0
        for label in label_list:
            predict_list = self.predict_label_dict[label]
            true_list = self.true_label_dict[label]
            if len(predict_list) != 0:
                sum_f1 += self.get_f1(true_list, predict_list)
                f1_num += 1
        #  0.918744101215
        macro_f1 = float(sum_f1) / len(label_list)
        #  0.95041322314
        # macro_f1 = float(sum_f1) / f1_num
        return macro_f1

    def get_micro_f1(self):
        label_list = self.true_label_dict.keys()
        sum_tp = 0
        sum_tp_plus_fp = 0
        sum_tp_plust_fn = 0
        for label in label_list:
            predict_list = self.predict_label_dict[label]
            true_list = self.true_label_dict[label]
            sum_tp += self.get_tp(true_list, predict_list)
            sum_tp_plus_fp += self.get_tp_plus_fp(predict_list)
            sum_tp_plust_fn += self.get_tp_plust_fn(true_list)
        micro_precision = float(sum_tp) / sum_tp_plus_fp
        micro_recall = float(sum_tp) / sum_tp_plust_fn

        return micro_precision, micro_recall, float(2 * micro_precision * micro_recall) / (
                    micro_precision + micro_recall)

    def get_tp(self, true_list, predict_list):
        tp = len(set(predict_list).intersection(set(true_list)))
        return tp

    def get_tp_plus_fp(self, predict_list):
        tp_plus_fp = len(predict_list)
        return tp_plus_fp

    def get_tp_plust_fn(self, true_list):
        tp_plust_fn = len(true_list)
        return tp_plust_fn


def print_dict(label_dict):
    for label, idx_list in label_dict.items():
        print idx_list


def print_paperid(f1):
    """
     1
     list1 = [6, 32, 50]
    list2 = [0, 39, 47, 57]
    list3 = [10, 35, 46, 63, 75]
    list4 = [27, 37, 42, 53, 58, 65, 73, 82, 96, 98]
    list5 = [95]
    list_set = [list1, list2, list3, list4, list5]

    """

    """

    """
    list1 = [9, 14, 44, 85]
    list2 = [11]

    list_set = [list1, list2]
    for ls in list_set:
        paperid_ls = []
        for idx in ls:
            paperid_ls.append(f1.idx_paperid_dict[idx])
        print paperid_ls


def print_cluster_list():
    xml2dict = Xml2Dict(xml_dir + xml_file_name7)
    metric = Metric_macro_micro(xml2dict)
    # print metric.cluster_list

    from second_stage_graph import print_clust_total
    print_clust_total(metric.cluster_list)
    print "====================="

    true_idx_list = metric.true_label_dict.values()
    for idx_list in true_idx_list:
        print idx_list, len(idx_list)


def print_f1():
    xml_file_name_list = [
        xml_file_name1,
        xml_file_name2,
        xml_file_name3,
        xml_file_name4,
        xml_file_name5,
        xml_file_name6,
        xml_file_name7,
        xml_file_name8,
        xml_file_name9,
        xml_file_name10
    ]
    for xml_file_name in xml_file_name_list:
        xml2dict = Xml2Dict(xml_dir + xml_file_name)
        metric = Metric_macro_micro(xml2dict)

        metric.print_true_predict_lable_dict()
        # print metric.clusterlen_sort_label_list
        # print metric.true_label_dict.values()
        # print xml_file_name, metric.get_macro_f1(), metric.get_micro_f1()
        # print xml_file_name, metric.get_micro_f1()
        #
        # print metric.get_micro_f1()


if __name__ == '__main__':
    print_f1()
    # print_cluster_list()

"""
Jing Zhang.xml (0.9583333333333334, 0.7965367965367965, 0.8699763593380614)
Bin Yu.xml (1.0, 0.6095238095238096, 0.7573964497041421)
Rakesh Kumar.xml (1.0, 0.8333333333333334, 0.9090909090909091)
Lei Wang.xml (0.9372693726937269, 0.8246753246753247, 0.8773747841105354)
Bin Li.xml (1.0, 0.8895027624309392, 0.9415204678362573)
Yang Wang.xml (0.9926470588235294, 0.6923076923076923, 0.8157099697885196)
Bo Liu.xml (1.0, 0.8790322580645161, 0.9356223175965666)
Yu Zhang.xml (1.0, 0.7319148936170212, 0.8452088452088452)
David Brown.xml (1.0, 0.9672131147540983, 0.9833333333333333)
Wei Xu.xml (1.0, 0.8104575163398693, 0.8953068592057761)
"""